package com.apes.fn.scm.lossSale;

import com.alibaba.fastjson.JSONObject;
import com.apes.fn.scm.retailOrder.model.RetailOrder;
import com.apes.fn.scm.retailOrder.model.RetailOrderPayMethod;
import com.apes.fn.scm.retailOrder.repository.RetailOrderRepository;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.ReflectUtil;
import com.apes.scm.masterdata.finance.model.BankAccount;
import com.apes.scm.masterdata.finance.repository.BankAccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author wuq
 * @Date 2021-12-6
 */
@Service
public class LossSaleAdjustPaymentService extends DomainService {

    @Autowired
    private LossSaleAdjustPaymentRepository lossSaleAdjustRepository;
    @Autowired
    private RetailOrderRepository retailOrderRepository;

    public LossSaleAdjustPayment save(SimpleRequest request) {
        LossSaleAdjustPayment lossSaleAdjust = request.getO(LossSaleAdjustPayment.class);
        if (StringUtils.isEmpty(lossSaleAdjust.getRetailId())) throw new RuntimeException("零售单号为空，请检查！");

        lossSaleAdjust.setState("sent");
        lossSaleAdjust.setCreator(request.getPersonId());
        lossSaleAdjust.setCreateDate(new Date());

        if (lossSaleAdjust.getCompany() == null) {
            lossSaleAdjust.setCompany(lossSaleAdjust.getDept().getCompany());
        }

        // 数据检查
        lossSaleAdjust.getPaymentMethodSet().forEach( LossSaleAdjustPayMethod::checkPayMethod );
        double amountTotal = lossSaleAdjust.getPaymentMethodSet().stream().mapToDouble(LossSaleAdjustPayMethod::getAmount).sum();
        if (amountTotal != 0) throw new RuntimeException("收款方式调整明细合计金额必须为0，请检查");

        // 缓存零售单单号
        RetailOrder retailOrder = findRetailOrder(lossSaleAdjust.getRetailId());
        lossSaleAdjust.setRetailId(retailOrder.getId());
        lossSaleAdjust.setCustomerExpand(retailOrder.getCustomerExpand());

        return lossSaleAdjustRepository.saveAndFlush(lossSaleAdjust);
    }

    // 修改retailId数据
    public void filterByRequest(SimpleRequest request){
        JSONObject jo = request.getJO();
        Object retailId = jo.get("retailId");
        if (retailId instanceof JSONObject) jo.put("retailId",((JSONObject) retailId).getString("ID"));
        request.setBody(jo.toJSONString());
    }

    public Map findOldPayMethod(SimpleRequest request) {
        String retailId = request.get("retailId");
        RetailOrder retailOrder = findRetailOrder(retailId);

        Map<String, Object> result = new HashMap<>();
        result.put("dept", MapUtil.mapped(retailOrder.getDept()));
        result.put("lossQuotationId", retailOrder.getSource());
        result.put("customerExpand", MapUtil.mapped(retailOrder.getCustomerExpand()));
        result.put("oldPayMethod", MapUtil.mapped(retailOrder.getPaymentMethodSet()));
        return result;
    }

    private RetailOrder findRetailOrder(String retailId) {
        RetailOrder retailOrder = retailOrderRepository.findOne(retailId);
        if (retailId == null) throw new RuntimeException("没有找到【"+ retailId +"】对应的零售单");
        if ( retailOrder.getScene() ==null || !"RO002".equals(retailOrder.getScene().getId())) {
            throw new RuntimeException("非赋能定损销售来源的单据，不允许调整");
        }
        return retailOrder;
    }

    public String delete(SimpleRequest request) {
        LossSaleAdjustPayment lossSaleAdjust = request.getO(LossSaleAdjustPayment.class);
        lossSaleAdjust.setDeleteId(request.getPerson());
        lossSaleAdjust.setDeleteDate(new Date());
        lossSaleAdjust.setState("delete");
        lossSaleAdjustRepository.saveAndFlush(lossSaleAdjust);
        return "删除成功";
    }

    public LossSaleAdjustPayment approve(SimpleRequest request) {
        LossSaleAdjustPayment lossSaleAdjust = request.getO(LossSaleAdjustPayment.class);

        lossSaleAdjust.setApproveUID(request.getPerson());
        lossSaleAdjust.setDateApprove(new Date());
        lossSaleAdjust.setState("approve");

        lossSaleAdjust = lossSaleAdjustRepository.saveAndFlush(lossSaleAdjust);

        // 生成零售单收款方式调整
        createRetailOrder(lossSaleAdjust);
        return lossSaleAdjust;
    }

    private void createRetailOrder(LossSaleAdjustPayment lossSaleAdjust) {
        String retailId = lossSaleAdjust.getRetailId();
        RetailOrder retailOrder = retailOrderRepository.findOne(retailId);

        RetailOrder newRetailOrder = new RetailOrder();
        newRetailOrder.setOrderType("lossPaymentUpdate");
        newRetailOrder.setAmtTotal(0);
        ReflectUtil.copy(retailOrder, newRetailOrder, "dept", "company", "moveType", "channel", "customerExpand", "claimType", "caseType", "insuranceCompany");
        newRetailOrder.setServiceDept(retailOrder.getServiceDept());
        newRetailOrder.setInsuranceReportNumber(retailOrder.getInsuranceReportNumber());
        newRetailOrder.setSourceBusinessType(LossSaleAdjust.class.getSimpleName());
        newRetailOrder.setOrigin(lossSaleAdjust.getId());
        newRetailOrder.setSource(lossSaleAdjust.getLossQuotationId());
        newRetailOrder.setRemark("通过定损调整单生成");
        newRetailOrder.setClerk(lossSaleAdjust.getApproveUID());
        newRetailOrder.setSaleCustomer(retailOrder.getSaleCustomer());
        newRetailOrder.setSaleItems(new HashSet<>());
        newRetailOrder.setDateApprove(new Date());
        newRetailOrder.setPostDate(new Date());

        newRetailOrder.setState("done");
        newRetailOrder.setScene(null);

        // 收款方式增加
        newRetailOrder.setPaymentMethodSet(lossSaleAdjust.getPaymentMethodSet().stream().map(
                lossSaleAdjustPayMethod -> {
                    RetailOrderPayMethod payMethod = new RetailOrderPayMethod();
                    payMethod.setOrder(newRetailOrder);
                    payMethod.setAmount(lossSaleAdjustPayMethod.getAmount());
                    payMethod.setBankAccount(lossSaleAdjustPayMethod.getBankAccount());
                    payMethod.setAccountNumber(lossSaleAdjustPayMethod.getAccountNumber());
                    payMethod.setCustomer(lossSaleAdjustPayMethod.getCustomer());
                    payMethod.setPaymentMethodType(lossSaleAdjustPayMethod.getPaymentMethodType());
                    return payMethod;
                }).collect(Collectors.toSet()));
        checkPaymentMethodAccount(newRetailOrder);
        // 不能走零售的逻辑，因为零售逻辑控制不允许其他应收调整
        retailOrderRepository.saveAndFlush(newRetailOrder);
    }

    private void checkPaymentMethodAccount(RetailOrder saleOrder) {
        saleOrder.getPaymentMethodSet().stream()
                .filter(payMethod -> "03".equals(payMethod.getPaymentMethodType().getId()))
                .forEach(payMethod -> {
                    if (StringUtils.isEmpty(payMethod.getAccountNumber()) && Objects.isNull(payMethod.getBankAccount())) {
                        throw new RuntimeException("转账的收款方式中，银行账号与银行账户字段同时为空，请检查");
                    }

                    // 如果银行账户与银行账号同时存在就 continue
                    if (!StringUtils.isEmpty(payMethod.getAccountNumber()) && Objects.nonNull(payMethod.getBankAccount()) && !StringUtils.isEmpty(payMethod.getBankAccount().getId()))
                        return;

                    /** 分别处理银行账号与账户 **/
                    if (Objects.nonNull(payMethod.getBankAccount()) && !StringUtils.isEmpty(payMethod.getBankAccount().getId())) {
                        // 根据银行账户获取银行账号
                        payMethod.setAccountNumber(payMethod.getBankAccount().getAccountId());
                    }

                    if (!StringUtils.isEmpty(payMethod.getAccountNumber())) {
                        // 根据银行账号获取账户编码
                        BankAccount bankAccount = SpringManager.getBean(BankAccountRepository.class)
                                .findAll(JpaDsl.toCriteriaByEq("accountId", payMethod.getAccountNumber()))
                                .stream().findFirst()
                                .orElseThrow(() -> new RuntimeException("根据银行账号没有找到对应银行的账户信息"));
                        payMethod.setBankAccount(bankAccount);
                    }
                });
    }
}
